package ginx

import (
	"net/http"

	"gitee.com/youkelike/zerror"
	"gitee.com/youkelike/zlog"
	"gitee.com/youkelike/zvalidation"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
)

// Wrap 用于无参数、不需要登录接口
func Wrap(l zlog.Logger, fn func(ctx *gin.Context) (Result, error)) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		res, err := fn(ctx)
		if err != nil {
			handleBizErr(ctx, l, &res, err)
		}

		ctx.JSON(ctx.Writer.Status(), res)
	}
}

// WrapToken 用于无参数、需要登录接口
func WrapToken[C jwt.Claims](l zlog.Logger, fn func(ctx *gin.Context, uc C) (Result, error)) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		val, ok := ctx.Get("users")
		if !ok {
			ctx.JSON(http.StatusUnauthorized, Result{Code: CodeUnauthorizedError, Msg: "未登录"})
			return
		}

		c, ok := val.(C)
		if !ok {
			ctx.JSON(http.StatusUnauthorized, Result{Code: CodeUnauthorizedError, Msg: "未登录"})
			return
		}

		res, err := fn(ctx, c)
		if err != nil {
			handleBizErr(ctx, l, &res, err)
		}

		ctx.JSON(ctx.Writer.Status(), res)
	}
}

// WrapBodyAndToken 用于有参数、需要登录接口
func WrapBodyAndToken[T any, C jwt.Claims](l zlog.Logger, validFunc zvalidation.ValidateFunc[T], fn func(ctx *gin.Context, req *T, uc C) (Result, error)) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		val, ok := ctx.Get("users")
		if !ok {
			ctx.JSON(http.StatusUnauthorized, Result{Code: CodeUnauthorizedError, Msg: "未登录"})
			return
		}

		c, ok := val.(C)
		if !ok {
			ctx.JSON(http.StatusUnauthorized, Result{Code: CodeUnauthorizedError, Msg: "未登录"})
			return
		}

		var req T
		// 请求参数绑定、验证
		err := HandleJSONRequest[T](ctx, &req, validFunc)
		if err != nil {
			l.Debugw("解析请求参数失败", "err", err)
			ctx.JSON(http.StatusBadRequest, Result{Code: CodeBadRequestError, Msg: "请求参数解析失败"})
			return
		}

		res, err := fn(ctx, &req, c)
		if err != nil {
			handleBizErr(ctx, l, &res, err)
		}

		ctx.JSON(ctx.Writer.Status(), res)
	}
}

// WrapBody 用于有参数、不需要登录接口
func WrapBody[T any](l zlog.Logger, validFunc zvalidation.ValidateFunc[T], fn func(ctx *gin.Context, req *T) (Result, error)) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		var req T
		// 请求参数绑定、验证
		err := HandleJSONRequest[T](ctx, &req, validFunc)
		if err != nil {
			l.Errorw("解析请求参数失败", "err", err)
			er := err.(*zerror.Zerror)
			ctx.JSON(http.StatusBadRequest, Result{Code: CodeBadRequestError, Msg: er.Message})
			return
		}

		// 拿验证后的请求参数去调用业务函数
		res, err := fn(ctx, &req)
		if err != nil {
			handleBizErr(ctx, l, &res, err)
		}

		ctx.JSON(ctx.Writer.Status(), res)
	}
}

func handleBizErr(ctx *gin.Context, l zlog.Logger, res *Result, err error) {
	l.Errorw("处理业务逻辑出错",
		zlog.String("path", ctx.Request.URL.Path),
		// 命中的路由
		zlog.String("route", ctx.FullPath()),
		zlog.Err(err))

	// 业务没有指定 http 响应码，默认用 500
	if ctx.Writer.Status() == http.StatusOK {
		ctx.Status(http.StatusInternalServerError)
	}

	// 业务没有指定错误码，默认用 500
	if res.Code == 0 {
		res.Code = CodeGeneralServerError
	}
}
